/*
--------------------------------------------------------------------------------
				Ak-Lib. 
			 Project       	: Ak-Lib
			 Group         	: Ak-Lib R&D Team
			 Author        	: Ocean.Wang
			 Create Date 	: 05-01-2010
--------------------------------------------------------------------------------
Code Name 	:  A.K 	~v~
Description	:  Yet Another Cross-Platform Lib.
Cautions    	:
--------------------------------------------------------------------------------
Change History:
Version		: 0.1
Building 		: 05/01/10
10-05.01 first draft, build, debug and test.
--------------------------------------------------------------------------------
*/

#include "ak_mc.h"

/* =====================================================================
**	Mc Item:
**	Let's Talk about the LRU & GC.
**	The GC is based on the Exp-Time.We have 2 type of GC-Node.
**	Pri-Exp Node & Sub-Exp Node.
**
**	Pri-Exp Node:
**	Be Indexing in an RB-Tree for fast searching.
**	Has a Que which contains all the same ExpTime Sub-Node.
**	
**	Sub-Exp Node:
**	Be Queue in the Pri-Exp Node's Expire Que.
**	All the Sub-Exp Nodes' exptime is the same &= Pri-Exp Node's.
**	
**	GC & LRU:
**	A background Thread will do Lazy GC.
**	1. Searching the Give ExpTime.
**	2. Get the Pri-Exp Node.
**	3. Clean the Pri-Exp Node's Expire Queue.
**	4. Clean the Pri-Exp Node.
**	
*/
#define _MC_ITEM_MAXSZ			0X400000

/* Item Type: Value */
#define _MC_ITEM_VALUE			0X00000000 	/* It's a Value */
#define _MC_ITEM_COUNT			0X00000001 	/* It's a Counter */
#define _MC_ITEM_V_MASK 		0X0000000F

/* Item Type: Allocator */
#define _MC_ITEM_ALC_NUL 		0X00000000
#define _MC_ITEM_ALC_SLAB 		0X00000010
#define _MC_ITEM_ALC_CHUNK		0X00000020
#define _MC_ITEM_ALC_HEAP		0X00000040
#define _MC_ITEM_A_MASK 		0X000000F0

/* =====================================================================
**	 Mc Page & Slab & Chunk & Pool
*/
/*
**	256, 	384, 	512, 	768, 
**	1k, 		1.5k, 	2k, 		3k,
**	4k, 		6k, 		8k, 		12k,
**	16k,		24k,		32k,		48k,
**	64k
*/

#define _MC_SLAB_PAGE 		0X100000	/* 1M Based */
#define _MC_SLAB_MAXSZ 		0X10000		/* Max 64K Item (Key - Value) */
#define _MC_SLAB_MINSZ		0X100		/* 256 */
#define _MC_SLAB_TBL  		17
static size_t  	_gMC_SlabSz[_MC_SLAB_TBL]={
	0X100, 	0X180, 	0X200, 	0X300,
	0X400, 	0X600,  0X800, 	0XC00,
	0X1000, 0X1800, 0X2000, 0X3000,
	0X4000, 0X6000, 0X8000, 0XC000,
	0X10000
};


/*
**	128k, 	192k, 	256k, 	384k, 
**	512k, 	768k, 	1M
**	
*/
#define _MC_CHUNK_PAGE 		0X800000	/* 8M Based */
#define _MC_CHUNK_MAXSZ 	0X100000	/* Max 1M Item (Key - Value) */
#define _MC_CHUNK_MINSZ		0X20000		/* 128K */
#define _MC_CHUNK_TBL  		7

static size_t  	_gMC_ChunkSz[_MC_CHUNK_TBL]={
	0X20000, 0X30000, 0X40000, 	0X60000,
	0X80000, 0XC0000, 0X100000
};

/* Heap & Pools */
typedef struct{
	/* Heap & Pool */
	_t_sysMHeap 	mHeap;
	_t_sysMPool 	mPool[2];
	
	/* Slots: Slabs & Chunks */
	_t_sysMSlot 	slabs[_MC_SLAB_TBL];
	_t_sysMSlot 	chunks[_MC_CHUNK_TBL];
}_t_mc_alk;

/* -------------------------------------------------------------
** Mem Cache's Pool
*/
#define _MC_MHEAP_MIN 	64
void * 		Mc_HeapInit(_t_sysMHeap ** ppHeap, size_t memLimit)
{	
	_t_mc_alk 		* pMcAlk;	
	_t_sysMHeap 	* pHeap;
	_t_sysMPool 	* pPool;
	_t_sysMSlot 	* pSlot;

	if(memLimit < _MC_MHEAP_MIN) 
		memLimit = _MC_MHEAP_MIN;

	/* Init the Alk */
	pMcAlk = SYS_CALLOC(1, sizeof(_t_mc_alk));
	
	pHeap = &(pMcAlk->mHeap);
	MHeap_Init(pHeap, memLimit);

	MHEAP_SET_POOL(pHeap, pMcAlk->mPool);
	MHEAP_SET_PCNT(pHeap, 2);

	/* Slab */
	pPool = &(pMcAlk->mPool[0]);
	MPool_Init(pPool, pHeap, 			_MC_SLAB_PAGE, 	_MC_SLAB_MAXSZ, 
		memLimit/16,	 memLimit/2, 	MPOOL_T_SLAB_GC);

	pSlot = pMcAlk->slabs;
	MSlab_Init(pPool, pSlot, _gMC_SlabSz, _MC_SLAB_TBL,	_MC_SLAB_MINSZ,	sizeof(_t_mc_item));
	
	/* Chunk */
	pPool = &(pMcAlk->mPool[1]);
	MPool_Init(pPool, pHeap, 			_MC_CHUNK_PAGE, 	_MC_CHUNK_MAXSZ, 
		memLimit/64,	 memLimit/4, 	MPOOL_T_SLAB_GN);

	pSlot = pMcAlk->chunks;
	MSlab_Init(pPool, pSlot, _gMC_ChunkSz, _MC_CHUNK_TBL,	_MC_CHUNK_MINSZ,	sizeof(_t_mc_item));

	*ppHeap = pHeap;
	return pMcAlk;
}

_t_mc_item * McItem_Alloc(_t_sysMHeap * pHeap, size_t  size)
{
	_t_mc_item 	* pItem;
	_t_sysMPool* pPool;
		
	pItem = NULL;
	if(size > 0){
		if(size <= _MC_SLAB_MAXSZ){
			/* Slab */
			pPool = &(pHeap->pools[0]);
			pItem = (_t_mc_item *)MPool_AllocEx(pPool, size);
			if(pItem){
				pItem->ci.mi.type = _MC_ITEM_ALC_SLAB;
			}
		}
		else if(size <= _MC_CHUNK_MAXSZ){
			/* Chunk */
			pPool = &(pHeap->pools[1]);
			pItem = (_t_mc_item *)MPool_AllocEx(pPool, size);
			if(pItem){
				pItem->ci.mi.type = _MC_ITEM_ALC_CHUNK;
			}
		}
		else if(size <= _MC_ITEM_MAXSZ){
			size += sizeof(_t_mc_item);
			
			if((pHeap->mAlloc + size)  <= pHeap->mLimit){
				pItem = (_t_mc_item *)SYS_MALLOC(size);
				if(pItem){
					pItem->ci.mi.type 		= _MC_ITEM_ALC_HEAP;
					pItem->ci.mi.alkSize	= size;
					/* Update */
					SYS_ENTER_CS(&(pHeap->lock));
					pHeap->mAlloc += size;
					SYS_LEAVE_CS(&(pHeap->lock));
				}
			}
		}
	}
	
	return pItem;
}

/* User should make sure Pool & Item Avail */
int 	McItem_Free(_t_sysLruCt * pBucket, _t_mc_item * pItem)
{
	unsigned int type;
	_t_sysMHeap * pHeap;
	_t_sysMPool	* pPool;

	if(	pBucket && pItem && 
		(pBucket->klMem) && 
		(pHeap = pBucket->klMem->mHeap)){
		type = (pItem->ci.mi.type & _MC_ITEM_A_MASK);
		switch(type){
			case _MC_ITEM_ALC_SLAB:
				pPool = &(pHeap->pools[0]);
				return MPool_FreeEx(pPool, (_t_sysMi *)pItem);
				break;
			case _MC_ITEM_ALC_CHUNK:
				pPool = &(pHeap->pools[1]);
				return MPool_FreeEx(pPool, (_t_sysMi *)pItem);
				break;
			case _MC_ITEM_ALC_HEAP:
				/* Update */
				SYS_ENTER_CS(&(pHeap->lock));
				pHeap->mAlloc -= pItem->ci.mi.alkSize;
				SYS_LEAVE_CS(&(pHeap->lock));
				SYS_FREE(pItem);
				return 0;
				break;
				
			default:
				break;
		}
	}
	return -1;
}

int 	McItem_FreeQ(_t_sysMHeap * pHeap, _t_blist * pQueue)
{
	unsigned int type;
	_t_sysMPool* pPool;
	_t_sysMi  	* pItem;
	
	if(pHeap && pQueue){
		while((pItem = (_t_sysMi *)BLST_GET(pQueue)) != NULL){
			type = (pItem->type & _MC_ITEM_A_MASK);
			switch(type){
				case _MC_ITEM_ALC_SLAB:
					pPool = &(pHeap->pools[0]);
					MPool_FreeEx(pPool, pItem);
					break;
				case _MC_ITEM_ALC_CHUNK:
					pPool = &(pHeap->pools[1]);
					MPool_FreeEx(pPool, pItem);
					break;
				case _MC_ITEM_ALC_HEAP:
					/* Update */
					SYS_ENTER_CS(&(pHeap->lock));
					pHeap->mAlloc -= pItem->alkSize;
					SYS_LEAVE_CS(&(pHeap->lock));
					SYS_FREE(pItem);
					break;
					
				default:
					break;
			}
		}
	}
	return -1;
}

int 	McHeap_GC(_t_sysMHeap * pHeap)
{
	_t_sysMPool* pPool;

	if(pHeap){
		/* Chunks */
		pPool = &(pHeap->pools[1]);
		if(pPool->bGC == 1){
			MPool_GC(pPool, pPool->pgIncr);
		}

		/* Slabs */
		pPool = &(pHeap->pools[0]);
		if(pPool->bGC == 1){
			MPool_GC(pPool, pPool->pgIncr);
		}
		return 0;
	}
	
	return -1;
}


/* ------------------------------------------------------------------
**	Mc Item Operations:
*/
int 	McItem_KeyComp(_t_rbnode * node1, _t_rbnode * node2)
{
	unsigned long long   key1;
	unsigned long long   key2;

	key1 = *((unsigned long long *)((_O_PARENT(_t_sysLruCi, mi.kNode, node1))->pKey));
	key2 = *((unsigned long long *)((_O_PARENT(_t_sysLruCi, mi.kNode, node2))->pKey));

	if(key1 > key2){
		return _TREE_GREAT;
	}else if(key1 < key2){
		return _TREE_LESS;
	}

	return _TREE_EQUAL;
}

int 	McItem_DataSet(_t_mc_item * pItem, _t_mc_data * pData, size_t lData)
{
	SYS_MEMCPY(pItem->pseudo, pData, lData);
	pItem->length = lData;
	return 0;
}

int 	McItem_DataGet(_t_mc_item * pItem, _t_mc_data * pData, size_t lData)
{
	if(lData > pItem->length){
		SYS_MEMCPY(pData, pItem->pseudo, pItem->length);
	}
	return 0;
}

int 	McItem_KeyBind(_t_mc_item * pItem)
{
	_t_mc_data * pData;
	if(pItem){
		pData = (_t_mc_data *)(pItem->pseudo);
		pItem->ci.pKey = &(pData->key);
		return 0;
	}
	return -1;
}

int 		McItem_Del(_t_sysLruCt * pBucket, void * pKey)
{
	_t_sysLruCi 	item;

	item.pKey = pKey;
	SLru_Rm(pBucket, &item);
	return 0;
}

_t_mc_item * McItem_Get(_t_sysLruCt * pBucket, void * pKey, void * pData, size_t lData)
{
	_t_mc_item   * 	pItem;
	_t_sysLruCi  *	pCi;
	_t_sysLruCi 	item;

	item.pKey = pKey;
	pItem = NULL;
	pCi = SLru_Get(pBucket, &item, pData, lData);
	if(pCi){
		pItem = _O_PARENT(_t_mc_item, ci, pCi);
	}
	return pItem;
}

_t_mc_item * McItem_Set(_t_sysLruCt * pBucket, void * pKey, void * pData, size_t lData, time_t expire)
{
	_t_mc_item 	* pItem;
	_t_sysLruCi	* pCi;
	_t_sysLruCi	* pClea;
	_t_sysKLMem* pKLMem;

	pKLMem = pBucket->klMem;
	pItem 	= NULL;
	if(pKLMem && (pKLMem->mHeap)){
		pItem	= McItem_Alloc(pKLMem->mHeap, lData);
		if(pItem){
			pItem->ci.pKey = pKey;
			if(expire > 0){
				pItem->ci.ti.expire = expire;
			}
			else{
				pItem->ci.ti.expire = 0;
			}
			/* Dangerous That We may Free the pItem in the Rep !*/
			pCi = SLru_Replace(pBucket, &(pItem->ci), &pClea, pData, lData);

			/* In fact, We must Get a New Ci */
			if(pCi){
				pItem = _O_PARENT(_t_mc_item, ci, pCi);
			}else{
				pItem = NULL;
			}
		}
	}
	
	return pItem;
}


/* ------------------------------------------------------------------
**	Mc Trans Operations:
*/
int 	McTrans_Tx(_t_mc_trans * pTrans, _t_sgbuf *	sgBuf, int sgCnt)
{
	return 0;
}

